package net.lastcoder.ifserver.database;

import org.logicalcobwebs.proxool.ConnectionPoolDefinitionIF;
import org.logicalcobwebs.proxool.ProxoolException;
import org.logicalcobwebs.proxool.ProxoolFacade;
import org.logicalcobwebs.proxool.admin.SnapshotIF;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.sql.Connection;
import java.sql.DatabaseMetaData;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.MissingResourceException;
import java.util.Properties;

/**
 * auth: WenYF
 * date: 2017/10/21
 */
public class LocalConnectionProvider implements ConnectionProvider {
    private static final Logger Log = LoggerFactory.getLogger(LocalConnectionProvider.class);
    private Properties settings;

    private boolean transactionsSupported;
    private boolean subqueriesSupported;
    private boolean scrollResultsSupported;
    private boolean streamTextRequired = false;
    private boolean maxRowsSupported = true;
    private boolean fetchSizeSupported = true;
    private boolean batchUpdatesSupported;

    /**
     * Creates a new DefaultConnectionProvider.
     */
    public LocalConnectionProvider(Properties properties) {
        settings = properties;

        start();

        Connection con = null;
        try {
            con = getConnection();
            setMetaData(con);
        }
        catch (MissingResourceException mre) {
            Log.error(mre.getMessage());
        }
        catch (Exception e) {
            Log.error(e.getMessage(), e);
        }
        finally {
            DbConnectionManager.closeConnection(con);
        }
    }

    @Override
    public boolean isPooled() {
        return true;
    }

    @Override
    public Connection getConnection() throws SQLException {
        Connection con = null;
        SQLException lastException = null;
        Integer currentRetryCount = 0;
        Integer maxRetries = 10;
        Integer retryWait = 250;
        do {
            try {
                try {
                    Class.forName("org.logicalcobwebs.proxool.ProxoolDriver");
                    String url = "proxool." + settings.getProperty("name") + ":com.mysql.jdbc.Driver:" + settings.getProperty("url");
                    con = DriverManager.getConnection(url, settings);
                } catch (ClassNotFoundException e) {
                    throw new SQLException(settings.getProperty("name") + " DbConnectionProvider: Unable to find driver: " + e);
                }
                if (con != null) {
                    return con;
                }
            } catch (SQLException e) {
                // TODO distinguish recoverable from non-recoverable exceptions.
                lastException = e;
                Log.info(settings.getProperty("name") + " Unable to get a connection from the database pool " +
                        "(attempt " + currentRetryCount + " out of " + maxRetries + ").", e);
            }

            try {
                Thread.sleep(retryWait);
            } catch (Exception e) {
                // Ignored, the thread was interrupted while waiting, so no need to log either
            }
            currentRetryCount++;
        } while (currentRetryCount <= maxRetries);

        throw new SQLException(settings.getProperty("name") + " LocalConnectionProvider.getConnection() " +
                "failed to obtain a connection after " + currentRetryCount + " retries. " +
                "The exception from the last attempt is as follows: " + lastException);
    }

    @Override
    public void start() {
    }

    @Override
    public void restart() {
    }

    @Override
    public void destroy() {
        settings = null;
    }
    @Override
    public String toString() {
        try {
            ConnectionPoolDefinitionIF poolDef = ProxoolFacade.getConnectionPoolDefinition(settings.getProperty("name"));
            SnapshotIF poolStats = ProxoolFacade.getSnapshot(settings.getProperty("name"), true);
            return poolDef.getMinimumConnectionCount()+","+poolDef.getMaximumConnectionCount()+","
                    +poolStats.getAvailableConnectionCount()+","+poolStats.getActiveConnectionCount();
        }
        catch (ProxoolException e) {
            Log.error(e.getMessage(), e);
            return settings.getProperty("name") + " Connection Provider";
        }
    }

    private void setMetaData(Connection con) throws SQLException {
        DatabaseMetaData metaData = con.getMetaData();
        // Supports transactions?
        transactionsSupported = metaData.supportsTransactions();
        // Supports subqueries?
        subqueriesSupported = metaData.supportsCorrelatedSubqueries();
        // Supports scroll insensitive result sets? Try/catch block is a
        // workaround for DB2 JDBC driver, which throws an exception on
        // the method call.
        try {
            scrollResultsSupported = metaData.supportsResultSetType(
                    ResultSet.TYPE_SCROLL_INSENSITIVE);
        }
        catch (Exception e) {
            scrollResultsSupported = false;
        }
        // Supports batch updates
        batchUpdatesSupported = metaData.supportsBatchUpdates();

        // Set defaults for other meta properties
        streamTextRequired = false;
        maxRowsSupported = true;
        fetchSizeSupported = true;

        // Get the database name so that we can perform meta data settings.
        String dbName = metaData.getDatabaseProductName().toLowerCase();
        String driverName = metaData.getDriverName().toLowerCase();

        // Oracle properties.
        if (dbName.indexOf("mysql") != -1) {
            transactionsSupported = false; /* TODO comment and test this, it should be supported since 5.0 */
        }
    }
}
